package jp.reflexworks.invoice.util;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.jdo.JDOCanRetryException;
import javax.jdo.JDOException;
import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;

import jp.reflexworks.gae.util.Condition;

import jp.reflexworks.gae.util.EntityConverter;
import jp.reflexworks.gae.util.KeyUtils;
import jp.reflexworks.gae.util.PMF;
import jp.reflexworks.gae.util.QueryUtils;
import jp.reflexworks.invoice.model.Invoice;
import jp.reflexworks.invoice.model.InvoiceBase;
import jp.reflexworks.invoice.model.Order;
import jp.sourceforge.reflex.util.FieldMapper;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;

public class JdoUtils {

	
	Logger logger = Logger.getLogger(this.getClass().getName());

	// Utils
	KeyUtils keyUtils = new KeyUtils(InvoiceBase.class);

	public static final int DEFAULT_PAGESIZE = 100;

	private static final int FLG_ORDER_DELETE = 1;
	private static final int FLG_ORDER_ACTIVE = 0;
	
	private static final String BR = System.getProperty("line.separator");
	
	/**
	 * InvoiceをPKであるinvoiceNoで１件検索する
	 * 
	 * @param param
	 * @return
	 */
	public Invoice getInvoiceByKey(Invoice param) {

		PersistenceManager pm = PMF.get().getPersistenceManager();
		Invoice invoice = pm.detachCopy(pm.getObjectById(Invoice.class, keyUtils.getChildKey(Invoice.class, param.invoiceNo)));
		pm.close();

		return invoice;
	}

	/**
	 * gaeのdatastoreのqueryを使用して検索する
	 * 
	 * @param param
	 * @return
	 */
	public Invoice getInvoiceById(Invoice param) {
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();

		Query query = new Query(Invoice.class.getSimpleName());
		query.addFilter("id", Query.FilterOperator.EQUAL, param.id);

		Entity entity = datastore.prepare(query).asSingleEntity();
		EntityConverter entityConverter = new EntityConverter();
		if (entity != null) {
			return (Invoice) entityConverter.convert(Invoice.class, entity);
		}
		return null;

	}

//もともとのcomplementInvoiceRecord()
//	/*
//	 * invoiceListに明細レコードをつける
//	 * 
//	 * InvoiceにOrderを詰める
//	 * 
//	 */
//	public void complementInvoiceRecord(List<Invoice> listInvoice) {
//
//		for (Invoice invoice : listInvoice) {
//			List<Order> listOrder = getOrdersByInvoiceNo(invoice.invoiceNo);
//			invoice.setOrder(listOrder);
//		}
//	}

	/*
	 * invoiceListに明細レコードをつける
	 * 
	 * InvoiceにOrderを詰める
	 * 
	 */
	public void complementInvoiceRecord(Invoice invoice) {

		//for (Invoice invoice : listInvoice) {
			List<Order> listOrder = getOrdersByInvoiceNo(invoice.invoiceNo);
			invoice.setOrder(listOrder);
		//}
	}
	
//	/*
//	 * invoiceListに明細レコードをつける
//	 * 
//	 * InvoiceにOrderを詰める
//	 * 
//	 */
//	public void complementInvoiceRecord(List<Invoice> listInvoice) {
//
//		for (Invoice invoice : listInvoice) {
//			
//			String invoiceNo = invoice.getInvoiceNo();
//			
//			List<Order> listOrder = getOrdersByInvoiceNo(invoiceNo);
//			
//			for (Order o: listOrder) {
//				o.setKey(createKeyOrder(invoiceNo, o.getId()));
//			}
//			
//			invoice.setOrder(listOrder);
//		}
//	}
	
	/**
	 * 明細レコードをinvoiceNoで検索する
	 * 
	 * @param invoiceNo
	 * @return
	 */
	public List<Order> getOrdersByInvoiceNo(String invoiceNo) {
		DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

		Query query = new Query(Order.class.getSimpleName());
		query.addFilter("invoiceNo", Query.FilterOperator.EQUAL, invoiceNo);
		query.addFilter("deleted", Query.FilterOperator.EQUAL, 0);

		Iterable<Entity> resultIterable = datastore.prepare(query).asIterable();
		EntityConverter entityConverter = new EntityConverter();

		Comparator comparator = new Comparator() {

			public int compare(Object o1, Object o2) {
				if (((Order) o1).getSeqno() == null || ((Order) o1).getSeqno().equals("")) {
					if (((Order) o2).getSeqno() == null || ((Order) o2).getSeqno().equals(""))
						return 0;
					else
						return 1;
				} else {
					if (((Order) o2).getSeqno() == null || ((Order) o2).getSeqno().equals(""))
						return -1;
					else {
						return ((Order) o1).getSeqno().compareTo(((Order) o2).getSeqno());
					}
				}
			}

		};

//		 //mura
//		 for (Entity e: resultIterable) {
//			 System.out.println("ITERABLE key:"+e.getProperty("key"));
//			 for (String k: e.getProperties().keySet()) {
//				 System.out.println(k + ": " + e.getProperty(k));
//			 }
//		 }

		return entityConverter.convert(Order.class, resultIterable, comparator);

	}

	/**
	 * gaeのdatastoreのqueryを使用して検索する
	 * 
	 * @param param
	 * @param pagesize
	 * @param nextId
	 * @return
	 */
	public List<Invoice> getEntriesByParam(Invoice param, String pagesize,
			String nextId) {

		/*
		 * // ここから検索用条件クラス作成 class ConditionImpl implements Condition{
		 * 
		 * public String dateString;
		 * 
		 * public ConditionImpl(String dateString) { this.dateString =
		 * dateString; }
		 * 
		 * @Override public boolean doCheck(Object param) {
		 * 
		 * // 条件にヒットした場合のみ、resultに追加する if
		 * (((Invoice)param).issuedDate!=null&&((Invoice
		 * )param).issuedDate.equals(dateString)) { return true; }
		 * 
		 * return false; } }; //ここまで
		 * 
		 * ConditionImpl condition = new ConditionImpl("20090725");
		 * 
		 * return getEntriesByParam(param, pagesize, nextId, condition);
		 */
		return getEntriesByParam(param, pagesize, nextId, null);
	}

	public List<Invoice> getEntriesByParam(Invoice param, String pagesize,
			String nextId, Condition condition) {
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();

		Query query = new Query(Invoice.class.getSimpleName());

		// 検索項目（Indexがあるもの） を指定してnewする
		QueryUtils queryUtils = new QueryUtils(new String[] { "invoiceNo",
				"companyName", "job", "issuedDate" });
		// paramの検索項目がnullでなければaddFiler(FilterOperator.EQUAL)される
		queryUtils.setParam(param, query);

		if (nextId != null) {
			query.addFilter("id", Query.FilterOperator.GREATER_THAN, Long
					.parseLong(nextId));
		}
		query.addSort("id");
		int limit = DEFAULT_PAGESIZE;

		if (pagesize != null) {
			limit = Integer.parseInt(pagesize);
		}

		// Fetch Option
		FetchOptions fetchOptions = FetchOptions.Builder.withLimit(limit);
		Iterable<Entity> resultIterable = datastore.prepare(query).asIterable(
				fetchOptions);

		EntityConverter entityConverter = new EntityConverter();
		List<Invoice> result = entityConverter.convert(Invoice.class,
				resultIterable, null, condition);

		return result;
	}


	/**
	 * 登録処理（複数件）
	 * 
	 * @param List
	 *            <Invoice> invoiceList
	 * @throws Exception
	 */
	public void insert(List<Invoice> invoiceList) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		int NUM_RETRIES = 3;

		try {
			for (int r = 0; r < NUM_RETRIES; r++) {
				try {
					tx.begin();
					logger.info("tx begin.");

					InvoiceBase invoiceBase;
					boolean invoiceBaseIsNew = true;
					try {
						invoiceBase = (InvoiceBase) pm.getObjectById(InvoiceBase.class, keyUtils.getBaseKeyBuilder().getKey());
						invoiceBaseIsNew = false;

					} catch (JDOObjectNotFoundException e) {
						invoiceBase = new InvoiceBase();
						invoiceBase.setKey(keyUtils.getBaseKeyBuilder().getKey());
					}

					for (Invoice invoice : invoiceList) {
						if (invoice != null) {
							invoiceBase.setInvoiceId(invoiceBase.getInvoiceId() + 1);
							invoice.setId(invoiceBase.getInvoiceId());

							// invoiceはinvoiceNoがPKなのでそれを入れている
							invoice.setKey(keyUtils.getChildKey(Invoice.class, invoice.invoiceNo));

							List<Order> orderList = invoice.getOrder();
							if (orderList != null && orderList.size() > 0) {

								long orderid = invoiceBase.getOrderId();
								for (Order order : orderList) {
									orderid++;
									order.setId(orderid);

									// keyの生成
									KeyFactory.Builder keybuiler = keyUtils.getChildKeyBuilder(Invoice.class, invoice.invoiceNo);
									Key key = (keybuiler.addChild(Order.class.getSimpleName(), keyUtils.getRecordKeyName(orderid))).getKey();
									order.setKey(key);
									
									debug("insert order key: " + key);
									
									order.setInvoiceNo(invoice.invoiceNo);
								}
								invoiceBase.setOrderId(orderid);

							}
							// これでinvoiceがpersistent
							invoiceBase.addInvoice(invoice);
						}
					}

					if (invoiceBaseIsNew) {
						pm.makePersistent(invoiceBase);
					}

					logger.info("commit.");
					tx.commit();
					break;

				} catch (JDOCanRetryException e) {
					if (r == (NUM_RETRIES - 1)) {
						throw e;
					}
					logger.info("rollbacked.");
					try {
						tx.rollback();
					} catch (Throwable ee) {
					}
				} catch (JDOException e) {
					logger.info("JDOException:" + e.getMessage());
					throw e;
				} catch (ConcurrentModificationException e) {
					logger.info("ConcurrentModificationException:"
							+ e.getMessage());
					throw e;
				}
			} // 　for loop

		} finally {
			if (tx.isActive()) {
				logger.info("rollbacked.");
				try {
					tx.rollback();
				} catch (Throwable e) {
				}
			}
			logger.info("pm closed.");
			try {
				pm.close();
			} catch (Throwable e) {
			}
		}
	}

	/**
	 * 登録処理（複数件）
	 * 
	 * @param List
	 *            <Invoice> invoiceList
	 * @throws Exception
	 */
	public void insert2(List<Invoice> invoiceList) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		int NUM_RETRIES = 3;

		try {
			for (int r = 0; r < NUM_RETRIES; r++) {
				try {
					tx.begin();
					logger.info("tx begin.");

					InvoiceBase invoiceBase;
					boolean invoiceBaseIsNew = true;
					try {
						invoiceBase = (InvoiceBase) pm.getObjectById(InvoiceBase.class, keyUtils.getBaseKeyBuilder().getKey());
						invoiceBaseIsNew = false;

					} catch (JDOObjectNotFoundException e) {
						invoiceBase = new InvoiceBase();
						invoiceBase.setKey(keyUtils.getBaseKeyBuilder().getKey());
					}

					for (Invoice invoice : invoiceList) {
						if (invoice != null) {
							invoiceBase.setInvoiceId(invoiceBase.getInvoiceId() + 1);
							invoice.setId(invoiceBase.getInvoiceId());

							// invoiceはinvoiceNoがPKなのでそれを入れている
							invoice.setKey(keyUtils.getChildKey(Invoice.class, invoice.invoiceNo));

							List<Order> orderList = invoice.getOrder();
							if (orderList != null && orderList.size() > 0) {

								long orderid = invoiceBase.getOrderId();
								for (Order order : orderList) {
									orderid++;
									order.setId(orderid);

									// keyの生成
									KeyFactory.Builder keybuiler = keyUtils.getChildKeyBuilder(Invoice.class, invoice.invoiceNo);
									Key key = (keybuiler.addChild(Order.class.getSimpleName(), keyUtils.getRecordKeyName(orderid))).getKey();
									order.setKey(key);
									
									debug("insert order key: " + key);
									
									order.setInvoiceNo(invoice.invoiceNo);
									pm.makePersistent(order);

								}
								invoiceBase.setOrderId(orderid);

							}
							// これでinvoiceがpersistent
							pm.makePersistent(invoice);
						}
					}

					if (invoiceBaseIsNew) {
						pm.makePersistent(invoiceBase);
					}

					logger.info("commit.");
					tx.commit();
					break;

				} catch (JDOCanRetryException e) {
					if (r == (NUM_RETRIES - 1)) {
						throw e;
					}
					logger.info("rollbacked.");
					try {
						tx.rollback();
					} catch (Throwable ee) {
					}
				} catch (JDOException e) {
					logger.info("JDOException:" + e.getMessage());
					throw e;
				} catch (ConcurrentModificationException e) {
					logger.info("ConcurrentModificationException:"
							+ e.getMessage());
					throw e;
				}
			} // 　for loop

		} finally {
			if (tx.isActive()) {
				logger.info("rollbacked.");
				try {
					tx.rollback();
				} catch (Throwable e) {
				}
			}
			logger.info("pm closed.");
			try {
				pm.close();
			} catch (Throwable e) {
			}
		}
	}

	/**
	 * 登録処理（1件）
	 * 
	 * @param invoice
	 *            invoice
	 * @throws Exception
	 */
	public void insert(Invoice invoice) {
		List<Invoice> invoiceList = new ArrayList<Invoice>();
		invoiceList.add(invoice);
		insert2(invoiceList);
	}

	/**
	 * 更新処理
	 * 
	 * @param invoice
	 *            product
	 * @throws Exception
	 */
	 
	public void update(Invoice invoice) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		FieldMapper fieldMapper = new FieldMapper();
		Transaction tx = pm.currentTransaction();
		int cntRetry = 3;

		try {
			for (int r = 0; r < cntRetry; r++) {
				try {
					
					tx.begin();
					if (invoice != null) {

						try {
							String invoiceNo = invoice.getInvoiceNo();
							long invoiceId = invoice.getId();

							// １．InvoiceとInvoiceBaseのPersistentオブジェクトをそれぞれ取得する
							Key keyInvoice = keyUtils.getChildKey(Invoice.class, invoiceNo);
							Invoice invoicePersistent = pm.getObjectById(Invoice.class, keyInvoice);
							InvoiceBase invoiceBasePersistent = pm.getObjectById(InvoiceBase.class, keyUtils.getBaseKeyBuilder().getKey());
							
							// ２．invoicePersistentから、いったんdetachCopy
							Invoice invoiceTarget = pm.detachCopy(invoicePersistent);
							
							// ３．コピーしたものに子要素のorderを補完追記
							invoiceTarget.setOrder(getOrdersByInvoiceNo(invoiceTarget.getInvoiceNo()));
							
							//　リビジョンのカウントアップで使用する
							long revisionTarget = invoiceTarget.getRevision();
							
							// 業務アプリによる楽観的ロック
							if (revisionTarget != invoice.getRevision()) {
								throw new ConcurrentModificationException("Optimistic lock!: " + revisionTarget + "/" +  invoice.getRevision());
							} else {
								
								//　４．invoiceからinvoiceSource(1品一葉)を取得する。orderIdの最大値を更新するためにinvoiceBasePersistentもパラメータとして渡す。
								Invoice invoiceSource = getInvoiceSource(invoice, invoiceBasePersistent);
								
								//　５．クライアントからのリクエストデータのうち更新があったものだけをinvoiceTargetにコピーする
								fieldMapper.setValue(invoiceSource, invoiceTarget);

								// ６．Revisionを更新
								invoiceTarget.setRevision(++revisionTarget);
								
								// ７．InvoiceBaseにaddすることでInvoice以下子要素のorderなどもpersistentになる(makePersistentは不要）
								invoiceBasePersistent.addInvoice(invoiceTarget);

								// addInvoiceは以下を実行している
								//if (invoiceBasePersistent.getInvoiceList()==null) {
								//	invoiceBasePersistent.setInvoiceList(new ArrayList<Invoice>());  
								//}
								//invoiceBasePersistent.getInvoiceList().add(invoiceTarget);
							}

						} catch (JDOObjectNotFoundException e) {
							debug("JDOObjectNotFoundException");
							// 0件更新
							throw e;
						}
					}
					
					debug("before tx.commit();");
					tx.commit();
					debug("after tx.commit();");
					break;
				} catch (JDOCanRetryException e) {
					debug("JDOCanRetryException");
					if (r == (cntRetry - 1)) {
						throw e;
					}
					//logger.info("rollbacked.");
					try {
						tx.rollback();
					} catch (Throwable ee) {
					}
				} catch (JDOException e) {
					debug("JDOException: " + e.getCause());
					//logger.info("JDOException:" + e.getMessage());
					throw e;
				} catch (ConcurrentModificationException e) {
					debug("ConcurrentModificationException_2: "+ e.getMessage());
					//logger.info("ConcurrentModificationException:"+ e.getMessage());
					throw e;
				}
			} // 　for loop
		} finally {
			debug("finally");
			if (tx.isActive()) {
				//logger.info("rollbacked.");
				try {
					tx.rollback();
					debug("tx.rollback();");
				} catch (Throwable e) {
					debug("catch tx.rollback();");
				}
			}
			//logger.info("pm closed.");
			try {
				pm.close();
				debug("pm.close();");
			} catch (Throwable e) {
				debug("catch pm.close();");
			}
		}
	}

	/**
	 * 更新処理
	 * 
	 * @param invoice
	 *            product
	 * @throws Exception
	 */
	public void update2(Invoice invoice) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		FieldMapper fieldMapper = new FieldMapper();
		Transaction tx = pm.currentTransaction();
		int cntRetry = 3;

		try {
			for (int r = 0; r < cntRetry; r++) {
				try {
					
					tx.begin();
					if (invoice != null) {

						try {
							String invoiceNo = invoice.getInvoiceNo();
							long invoiceId = invoice.getId();

							// １．InvoiceとInvoiceBaseのPersistentオブジェクトをそれぞれ取得する
							Key keyInvoice = keyUtils.getChildKey(Invoice.class, invoiceNo);
							Invoice invoicePersistent = pm.getObjectById(Invoice.class, keyInvoice);
							InvoiceBase invoiceBasePersistent = pm.getObjectById(InvoiceBase.class, keyUtils.getBaseKeyBuilder().getKey());

							//　リビジョンのカウントアップで使用する
							long revisionTarget = invoicePersistent.getRevision();
							
							// 業務アプリによる楽観的ロック
							if (revisionTarget != invoice.getRevision()) {
								throw new ConcurrentModificationException("Optimistic lock!: " + revisionTarget + "/" +  invoice.getRevision());
							} else {
								
								//　２．クライアントからのリクエストデータのうち更新があったものだけをinvoicePersistentに反映させる
								fieldMapper.setValue(invoice, invoicePersistent);

								// ３．Revisionを更新
								invoicePersistent.setRevision(++revisionTarget);

								//　４．invoiceからinvoiceSource(1品一葉)を取得する。orderIdの最大値を更新するためにinvoiceBasePersistentもパラメータとして渡す。
								Invoice invoiceSource = getInvoiceSource(invoice, invoiceBasePersistent);

								// ５．InvoiceSource子要素のorderが存在していれば更新する。新規であればmakepersistentする。
								for(Order order:invoiceSource.getOrder()) {
									try {
										Order orderPersistent = (Order) pm.getObjectById(Order.class, order.getKey());
										fieldMapper.setValue(order, orderPersistent);
										debug("order:"+order.getDeleted()+" id:"+order.getId()+" keyid:"+order.getKey().getId());
										
									} catch (JDOObjectNotFoundException e) {
										pm.makePersistent(order);
									}
								}
							}

						} catch (JDOObjectNotFoundException e) {
							debug("JDOObjectNotFoundException");
							// 0件更新
							throw e;
						}
					}
					
					debug("before tx.commit();");
					tx.commit();
					debug("after tx.commit();");
					break;
				} catch (JDOCanRetryException e) {
					debug("JDOCanRetryException");
					if (r == (cntRetry - 1)) {
						throw e;
					}
					logger.info("rollbacked.");
					try {
						tx.rollback();
					} catch (Throwable ee) {
					}
				} catch (JDOException e) {
					debug("JDOException: " + e.getCause());
					//logger.info("JDOException:" + e.getMessage());
					throw e;
				} catch (ConcurrentModificationException e) {
					debug("ConcurrentModificationException_2: "+ e.getMessage());
					//logger.info("ConcurrentModificationException:"+ e.getMessage());
					throw e;
				}
			} // 　for loop
		} finally {
			debug("finally");
			if (tx.isActive()) {
				//logger.info("rollbacked.");
				try {
					tx.rollback();
					debug("tx.rollback();");
				} catch (Throwable e) {
					debug("catch tx.rollback();");
				}
			}
			//logger.info("pm closed.");
			try {
				pm.close();
				debug("pm.close();");
			} catch (Throwable e) {
				debug("catch pm.close();");
			}
		}
	}

	
	private Invoice getInvoiceSource(Invoice invoice, InvoiceBase invoiceBasePersistent) {
		
		OrderConverter orderConverter = new OrderConverter();
		
		String invoiceNo = invoice.getInvoiceNo();
		Long invoiceId = invoice.getId();
		Key keyInvoice = keyUtils.getChildKey(Invoice.class, invoiceNo);
		Long currentOrderId = invoiceBasePersistent.getOrderId();
		
		//productcdをキーとしたorderIDのmapを作成
		Map<String, List<String>> mapOrderId = createMapProductCdOrderId(invoiceNo);
		
        List<Order> listSourceOrderReduced = invoice.getOrder();
		Map<String, Order> mapOrderDiff = orderConverter.diff(invoiceNo,listSourceOrderReduced);
		

		List<Order> listOrderDiff = new ArrayList<Order>();
		listOrderDiff.addAll(mapOrderDiff.values());
		
		List<Order> listOrderDiffExpanded = orderConverter.expand(listOrderDiff);
		
		for (Order order: listOrderDiffExpanded) {
			String productcd = order.getProductcd();
			List<String> ids = mapOrderId.get(productcd);
			
			if (order.getDeleted() == FLG_ORDER_DELETE) {
				String orderId = ids.remove(0);
				order.setId(Long.valueOf(orderId));
				Key keyOrder = createKeyOrder(order.getInvoiceNo(), orderId);
				order.setKey(keyOrder);
				
			} else if (order.getDeleted() == FLG_ORDER_ACTIVE) {
				++currentOrderId;
				order.setId(currentOrderId);
				Key keyOrder = createKeyOrder(order.getInvoiceNo(), currentOrderId);
				order.setKey(keyOrder);

			} else {
				throw new ConcurrentModificationException("order.getDeleted() failed.");
			}
		}	
		
		invoice.setKey(keyInvoice);
		invoice.setId(invoiceId);
		invoice.setOrder(listOrderDiffExpanded);
		invoiceBasePersistent.setOrderId(currentOrderId);
		return invoice;
	}
	

	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	

//元々のupdate(
//	/**
//	 * 更新処理
//	 * 
//	 * @param invoice
//	 *            product
//	 * @throws Exception
//	 */
//	public void update(Invoice invoice) {
//		PersistenceManager pm = PMF.get().getPersistenceManager();
//		FieldMapper fieldMapper = new FieldMapper();
//		Transaction tx = pm.currentTransaction();
//		int NUM_RETRIES = 3;
//
//		try {
//			for (int r = 0; r < NUM_RETRIES; r++) {
//				try {
//					
//					tx.begin();
//					logger.info("tx begin.");
//
//					if (invoice != null) {
//
//						try {
//
//							Invoice target = pm.getObjectById(Invoice.class, keyUtils.getChildKey(Invoice.class, invoice.invoiceNo));
//
//							// 業務アプリによる楽観的ロック
//							if (target.getRevision() != invoice.getRevision()) {
//
//								throw new ConcurrentModificationException();
//							} else {
//								
//								// 更新があるものだけをセット
//								fieldMapper.setValue(invoice, target);
//								// 更新カウントアップ
//								target.setRevison(target.getRevision() + 1);
//								
//								
//							}
//
//						} catch (JDOObjectNotFoundException e) {
//							// 0件更新
//							throw e;
//						}
//					}
//					logger.info("commit.");
//					tx.commit();
//					break;
//
//				} catch (JDOCanRetryException e) {
//					if (r == (NUM_RETRIES - 1)) {
//						throw e;
//					}
//					logger.info("rollbacked.");
//					try {
//						tx.rollback();
//					} catch (Throwable ee) {
//					}
//				} catch (JDOException e) {
//					logger.info("JDOException:" + e.getMessage());
//					throw e;
//				} catch (ConcurrentModificationException e) {
//					logger.info("ConcurrentModificationException:"
//							+ e.getMessage());
//					throw e;
//				}
//			} // 　for loop
//		} finally {
//			if (tx.isActive()) {
//				logger.info("rollbacked.");
//				try {
//					tx.rollback();
//				} catch (Throwable e) {
//				}
//			}
//			logger.info("pm closed.");
//			try {
//				pm.close();
//			} catch (Throwable e) {
//			}
//		}
//	}
	

	/**
	 * 削除処理(論理削除)
	 * 
	 * @param invoice
	 *            invoice
	 * @throws Exception
	 */
	public void delete(Invoice invoice) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		int NUM_RETRIES = 3;

		try {
			for (int r = 0; r < NUM_RETRIES; r++) {
				try {
					tx.begin();
					logger.info("tx begin.");

					if (invoice != null) {
						try {
							Invoice target = pm.getObjectById(Invoice.class,
									keyUtils.getChildKey(Invoice.class,
											invoice.invoiceNo));
							target.setDeleted(1); // 1 = deleted
						} catch (JDOObjectNotFoundException e) {
							// 0件更新
							throw e;
						}
					}

					logger.info("commit.");
					tx.commit();
					break;

				} catch (JDOCanRetryException e) {
					if (r == (NUM_RETRIES - 1)) {
						throw e;
					}
					logger.info("rollbacked.");
					try {
						tx.rollback();
					} catch (Throwable ee) {
					}
				} catch (JDOException e) {
					logger.info("JDOException:" + e.getMessage());
					throw e;
				} catch (ConcurrentModificationException e) {
					logger.info("ConcurrentModificationException:"
							+ e.getMessage());
					throw e;
				}
			} // 　for loop
		} finally {
			if (tx.isActive()) {
				logger.info("rollbacked.");
				try {
					tx.rollback();
				} catch (Throwable e) {
				}
			}
			logger.info("pm closed.");
			try {
				pm.close();
			} catch (Throwable e) {
			}
		}
	}

	/**
	 * 削除処理（物理削除）
	 * 
	 * @param invoice
	 * @throws Exception
	 */
	public void deleteCompletely(Invoice invoice) {

		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();

		try {
			if (invoice != null) {
				datastore.delete(keyUtils.getChildKey(Invoice.class,
						invoice.invoiceNo));
			}
		} catch (IllegalArgumentException e) {
			// 何もしない
		} catch (ConcurrentModificationException e) {
			// 何もしない
		}
	}

//	private void debug(Object message) {
//		System.out.println("DEBUG " + this.getClass().getName() + ": " + message);
//	}

	
	
//mura
	/*
	 * productcodeをキーとしたidのmapを作成
	 * map<productcode, List<id>>
	 * 
	 */
	public Map<String, List<String>> createMapProductCdOrderId(String InvoiceNo) {
		
		List<Order> listTargetOrderExpanded = getOrdersByInvoiceNo(InvoiceNo);
		
		Map<String, List<String>> mapOrderId = new HashMap<String, List<String>>();
		for (Order order: listTargetOrderExpanded) {
			String productcd = order.getProductcd();

			if (mapOrderId.get(productcd) == null) {
				mapOrderId.put(productcd, (new ArrayList<String>()));
			}
			(mapOrderId.get(productcd)).add(""+order.getId());
			//System.out.println("DEBUG: "+ order.getProductcd() + "/" + order.getId()  + "/" + order.getDescription()  + "/" + order.getInvoiceNo() + "/" + order.getQuantity());
		}
		
		return mapOrderId;
	}

	public Key createKeyOrder(String invoiceNo, String orderId) {
		return createKeyOrder(invoiceNo, Long.parseLong(orderId));
	}

	/**
	 * Orderのキーを生成する
	 * 
	 * @param invoiceNo
	 * @param orderId
	 * @return keyOrder
	 */
	public Key createKeyOrder(String invoiceNo, Long orderId) {

		KeyFactory.Builder keybuiler = keyUtils.getChildKeyBuilder(Invoice.class, invoiceNo);
		Key keyOrder = (keybuiler.addChild(Order.class.getSimpleName(), keyUtils.getRecordKeyName(orderId))).getKey();
		
		return keyOrder;
	}
	
	/**
	 * 現在のorderId（最大値）を返す
	 * @param pm
	 * @return
	 */
	public Long getCurrentOrderId(PersistenceManager pm) {
		InvoiceBase invoiceBase = (InvoiceBase) pm.getObjectById(InvoiceBase.class, keyUtils.getBaseKeyBuilder().getKey());		
		return invoiceBase.getOrderId();
	}

	
	
	
	
	
	private void debug(Object message) {
		//System.out.println("DEBUG " + this.getClass().getName() +  " :" + message);
		System.out.println(message);
	}
	
	private String inspectOrder(Order order) {
		StringBuilder sb = new StringBuilder();
		
		sb.append("      ## debugOrder-- ##" + BR);
		sb.append("      key: " + order.getKey() + BR);
		sb.append("      id: " + order.getId() + BR);
		sb.append("      invoiceNo: " + order.invoiceNo + BR);
		sb.append("      productcd: " + order.productcd + BR);
		sb.append("      deleted: " + order.deleted + BR);
		
		sb.append("      description: " + order.description + BR);
		sb.append("      lineTotal: " + order.lineTotal + BR);
		sb.append("      quantity: " + order.quantity + BR);
		sb.append("      seqno: " + order.seqno + BR);
		sb.append("      unitPrice: " + order.unitPrice + BR);
		sb.append("      ## --debugOrder ##" + BR);
		
		return sb.toString();
	}
	
	private String inspectListOrder(List<Order> listOrder) {
		StringBuilder sb = new StringBuilder();
		
		int len = listOrder.size();
		
		sb.append("  ## debugListOrder-- ##" + BR);
		sb.append("  listOrder.size(): " + len + BR);
		
		for (int i = 0; i < len; i++) {
			sb.append("    #order(" + i + ")--#" + BR);
			sb.append(inspectOrder(listOrder.get(i)));
			sb.append("    #--order(" + i + ")#" + BR);
		}

		sb.append("  ## --debugListOrder ##" + BR);
		
		return sb.toString();
	}
	
	private String inspectInvoice(Invoice invoice) {
		
		StringBuilder sb = new StringBuilder();
		
		sb.append("## debugInvoice-- ##" + BR);
		
		if (invoice.order != null) {
			sb.append("  orderList.size(): " + invoice.order.size() + BR);
		} else {
			sb.append("  orderList.size(): orderListがNULLです" + BR);
		}
		
		sb.append("  key: " + invoice.getKey() + BR);
		sb.append("  revision: " + invoice.revision + BR);
		sb.append("  id: " + invoice.id + BR);
		sb.append("  invoiceNo: " + invoice.invoiceNo + BR);
		sb.append("  deleted: " + invoice.deleted + BR);
		
		
		sb.append("  companyName: " + invoice.companyName + BR);
		sb.append("  address: " + invoice.address + BR);
		sb.append("  description: " + invoice.description + BR);
		sb.append("  invoiceTitle: " + invoice.invoiceTitle + BR);
		sb.append("  issuedDate: " + invoice.issuedDate + BR);
		sb.append("  job: " + invoice.job + BR);
		sb.append("  lineTotal: " + invoice.lineTotal + BR);
		sb.append("  myCompanyName: " + invoice.myCompanyName + BR);
		sb.append("  note: " + invoice.note + BR);
		sb.append("  note1: " + invoice.note1 + BR);
		sb.append("  note2: " + invoice.note2 + BR);
		sb.append("  note3: " + invoice.note3 + BR);
		sb.append("  phone: " + invoice.phone + BR);
		sb.append("  quantity: " + invoice.quantity + BR);
		
		sb.append("  salestax: " + invoice.salestax + BR);
		sb.append("  salestax1: " + invoice.salestax1 + BR);
		sb.append("  seqno: " + invoice.seqno + BR);
		sb.append("  subtotal: " + invoice.subtotal + BR);
		sb.append("  subtotal1: " + invoice.subtotal1 + BR);
		sb.append("  summary: " + invoice.summary + BR);
		sb.append("  title: " + invoice.title + BR);
		sb.append("  total: " + invoice.total + BR);
		sb.append("  totalTd: " + invoice.totalTd + BR);
		sb.append("  totalTd1: " + invoice.totalTd1 + BR);
		sb.append("  unitPrice: " + invoice.unitPrice + BR);
		sb.append("  zip: " + invoice.zip + BR);
		
		
		
		if (invoice.order != null) {
			sb.append(inspectListOrder(invoice.order));
		} else {
			sb.append("  orderListがNULLのためinspectListOrder()は実行されません" + BR);
		}
		
		sb.append("## --debugInvoice ##" + BR);
		
	
		return sb.toString();
	}
	
}
